﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using LiveEduWizard.Filter;
using System.ComponentModel;

namespace LiveEduWizard
{
    public partial class Timetable : UserControl, INotifyPropertyChanged
    {
        public struct TimeRange
        {
            public TimeRange(int start, int end)
            {
                if (start < 0)
                    start = 0;

                if (end < 0)
                    end = 0;

                if (start > 23)
                    start = 23;

                if (end > 23)
                    end = 23;

                if (end < start)
                    end = start;

                this.start = start;
                this.end = end;
            }

            private int start;

            public int Start
            {
                get { return start; }
            }

            private int end;

            public int End
            {
                get { return end; }
            }
        }

        private const int RowHeight = 90;

        private readonly NotifyCollectionChangedEventHandler CollectionChanged;

        private List<TimetableItem> timetableItems = new List<TimetableItem>();

        private IFilterRule filterRule;

        public Timetable()
        {
            CollectionChanged = new NotifyCollectionChangedEventHandler(items_CollectionChanged);
            
            InitializeComponent();
        }

        public Style TimeStyle { get; set; }

        private ObservableCollection<AppointmentViewModel> items;
        public ObservableCollection<AppointmentViewModel> Items
        {
            get { return items; }
            set 
            {
                if (items != value)
                {
                    if (items != null)
                    {
                        items.CollectionChanged -= CollectionChanged;
                    }

                    items = value;

                    foreach (var item in timetableItems)
                        LayoutRoot.Children.Remove(item);

                    timetableItems.Clear();

                    if (items != null)
                    {
                        items.CollectionChanged += CollectionChanged;

                        foreach (var item in items)
                        {
                            var timetableItem = new TimetableItem(item, range, RowHeight);
                            timetableItems.Add(timetableItem);
                            LayoutRoot.Children.Add(timetableItem);

                            ApplyFilter(timetableItem);
                        }
                    }

                    NotifyPropertyChanged("HasVisibleItem");
                }
            }
        }

        void items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    for (int i = 0; i < e.NewItems.Count; i++)
                    {
                        var timetableItem = new TimetableItem((AppointmentViewModel)e.NewItems[i], range, RowHeight);
                        LayoutRoot.Children.Insert(LayoutRoot.Children.Count - timetableItems.Count + e.NewStartingIndex + i, timetableItem);
                        timetableItems.Insert(i + e.NewStartingIndex, timetableItem);

                        ApplyFilter(timetableItem);
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    for (int i = 0; i < e.OldItems.Count; i++)
                    {
                        LayoutRoot.Children.Remove(timetableItems[e.OldStartingIndex + i]);                        
                    }
                    timetableItems.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
                    break;
                case NotifyCollectionChangedAction.Replace:
                    for (int i = 0; i < e.OldItems.Count; i++)
                    {
                        LayoutRoot.Children.Remove(timetableItems[e.OldStartingIndex + i]);                        
                    }
                    timetableItems.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
                    for (int i = 0; i < e.NewItems.Count; i++)
                    {
                        var timetableItem = new TimetableItem((AppointmentViewModel)e.NewItems[i], range, RowHeight);
                        LayoutRoot.Children.Insert(LayoutRoot.Children.Count - timetableItems.Count + e.NewStartingIndex + i, timetableItem);
                        timetableItems.Insert(i + e.NewStartingIndex, timetableItem);

                        ApplyFilter(timetableItem);
                    }
                    break;
                case NotifyCollectionChangedAction.Reset:
                    foreach (var item in timetableItems)
                        LayoutRoot.Children.Remove(item);

                    timetableItems.Clear();
                    
                    foreach (var item in items)
                    {
                        var timetableItem = new TimetableItem(item, range, RowHeight);
                        timetableItems.Add(timetableItem);
                        LayoutRoot.Children.Add(timetableItem);

                        ApplyFilter(timetableItem);
                    }
                    break;
            }

            NotifyPropertyChanged("HasVisibleItem");
        }

        private TimeRange range = new TimeRange(0,0);

        public TimeRange Range
        {
            get { return range; }
            set 
            {
                if (range.Start != value.Start || range.End != value.End)
                {
                    range = value;

                    LayoutRoot.Children.Clear();
                    LayoutRoot.RowDefinitions.Clear();

                    for (int i = range.Start; i <= range.End; i++)
                    {
                        LayoutRoot.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(RowHeight) });

                        if (i != range.End)
                        {
                            Line border = new Line();
                            border.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                            border.VerticalAlignment = System.Windows.VerticalAlignment.Bottom;
                            border.StrokeThickness = VerticalBorder.StrokeThickness;
                            border.StrokeDashArray.Add(3);
                            border.StrokeDashArray.Add(2);
                            border.Stroke = VerticalBorder.Stroke;
                            border.X2 = 500;
                            Grid.SetColumnSpan(border, 2);
                            Grid.SetRow(border, i - range.Start);
                            LayoutRoot.Children.Add(border);
                        }

                        TextBlock time = new TextBlock();                        
                        time.Text = string.Format("{0}:00", i);
                        time.Style = TimeStyle;
                        time.Margin = new Thickness(5, 5, 0, 0);
                        time.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
                        time.VerticalAlignment = System.Windows.VerticalAlignment.Top;                        
                        Grid.SetColumn(time, 0);
                        Grid.SetRow(time, i - range.Start);
                        LayoutRoot.Children.Add(time);
                    }

                    VerticalBorder.Y2 = (range.End - range.Start + 1) * RowHeight;
                    LayoutRoot.Children.Add(VerticalBorder);

                    foreach (var item in timetableItems)
                    {
                        item.UpdatePosition(range, RowHeight);
                        LayoutRoot.Children.Add(item);
                    }
                }
            }
        }

        public bool HasVisibleItem
        {
            get
            {
                foreach (var timetableItem in timetableItems)
                    if (timetableItem.Visibility == System.Windows.Visibility.Visible)
                        return true;

                return false;
            }
        }

        public void ApplyFilter(Filter.IFilterRule filterRule)
        {
            this.filterRule = filterRule;

            foreach (var timetableItem in timetableItems)
            {
                ApplyFilter(timetableItem);
            }

            NotifyPropertyChanged("HasVisibleItem");
        }

        private void ApplyFilter(TimetableItem timetableItem)
        {
            if (filterRule == null || filterRule.IsMatch(timetableItem.Appointment))
                timetableItem.Visibility = System.Windows.Visibility.Visible;
            else
                timetableItem.Visibility = System.Windows.Visibility.Collapsed;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (null != handler)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }        
    }
}
